﻿using System;
using System.Collections.Generic;
using System.Reflection;
using FluentlyXml.Core.Deserialization.BuiltInDeserializers;

namespace FluentlyXml.Core.Deserialization
{
	internal class TypeDeserializationConfig
	{
		private PropertyDeserializerDictionary _propertyDeserializers;
		private Func<object, object> _identitySelector = o => o;
		private List<Action<object>> _callbacks = new List<Action<object>>();

		public Type AppliesToType { get; private set; }

		public TypeDeserializationConfig(Type type)
		{
			AppliesToType = type;

			InitializePropertyBehaviors(type);
		}

		private void InitializePropertyBehaviors(Type type)
		{
			_propertyDeserializers = new PropertyDeserializerDictionary();

			foreach (var property in type.GetPropertiesForSerialization())
			{
				_propertyDeserializers[property] = DeserializationBehaviors.Default;
			}
		}

		public void SetIdentityFunction(Func<object, object> identitySelector)
		{
			_identitySelector = identitySelector;
		}

		public void AddPropertyDeserializer(PropertyInfo property, PropertyDeserializer propertyDeserializer)
		{
			_propertyDeserializers[property] = propertyDeserializer;
		}

		public ITypeDeserializer BuildDeserializer()
		{
			//TODO: We could pass the dictionary and what-not to the deserializer, but then changes to the 
			//config would immediately affect the existing deserializers, which may not be what we want.
			//Need to think more on this...
			var deserializer = new CustomDeserializer(AppliesToType);

			foreach (var propDeserializers in _propertyDeserializers)
			{
				deserializer.AddPropertyDeserializer(propDeserializers.Key, propDeserializers.Value);
			}

			foreach (var callback in _callbacks)
			{
				deserializer.AddPostDeserializationCallback(callback);
			}
			
			deserializer.SetIdentityFunction(_identitySelector);

			return deserializer;
		}

		public void MergeWith(TypeDeserializationConfig typeDeserializationConfig)
		{
			_identitySelector = typeDeserializationConfig._identitySelector;

			_callbacks.AddRange(typeDeserializationConfig._callbacks);

			//TODO: This is not what we want, but it's effectively what we're doing since
			//		the dictionary is completely populated when the config is constructed.  
			//		Whoops. 
			_propertyDeserializers = typeDeserializationConfig._propertyDeserializers;
		}

		public void AddPostDeserializationCallback(Action<object> callback)
		{
			_callbacks.Add(callback);
		}
	}
}